/* $$Id: RssFeedParser.java 24 2008-01-08 16:19:22Z qxodream $$
 *
 * RssReaderMidlet --  RSS reader for J2ME(midp)
 * The original codes of this porgram is from
 * RssOwlMIDlet(zhengyun@gmail.com)
 *
 * Copyright (c) 2007-2008 QXO(qxodream@gmail.com)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package com.ultrapower.model;

import java.io.IOException;
import java.io.InputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import com.tinyline.util.GZIPInputStream;
import com.ultrapower.control.GUIController;
import com.ultrapower.view.RssFeedOpen;

/**
 * 类说明：获取RSS XML并解析的类 <br/> RssFeedParser is an utility class for aquiring and
 * parsing a RSS feed. HttpConnection is used to fetch RSS feed and kXML is used
 * on xml parsing.
 *
 * @author Tommi Laukkanen
 * @author QXO(qxodream@gmail.com)
 *
 */
public class RssFeedParser {

	private GUIController controller;
	private RssFeed m_rssFeed; // The RSS feed
	// private XmlPullParser m_xmlParser = new KXmlParser(); // The Xml parser

	public RssFeedOpen openForm;

	/** Create new instance of RssDocument */
	public RssFeedParser(RssFeed rssFeed, GUIController control) {
		m_rssFeed = rssFeed;
		controller = control;
	}

	/** Return RSS feed */
	public RssFeed getRssFeed() {
		return m_rssFeed;
	}

	/***************************************************************************
	 * // RssFeedParser::parseRssFeed // // Description: // send a GET request
	 * to web server // // Parameters: // Return Values: // Remark:
	 * 经过试验,我们千万不能加入"User-Agent"Header字段
	 *
	 * //创建人: Tommi Laukkanen //修改者: zhengyun@ultrapower 2005.10.07 //
	 **************************************************************************/
	public boolean parseRssFeed() throws IOException, Exception {
		System.out.println("/** Enter parseRssFeed */");

		boolean bParseOk = false;
		HttpConnection hc = null;
		// String response = "";
		try {
			/**
			 * Open an HttpConnection with the Web server The default request
			 * method is GET
			 */
			String url = m_rssFeed.getUrl();
			System.out.println("/** Connect to:" + url);
			hc = (HttpConnection) Connector.open(url);
			hc.setRequestMethod(HttpConnection.GET);
			// hc.setRequestMethod(HttpConnection.POST);
			/** Some web servers requires these properties */

			/*
			 * 这博客堂/博客园两个站点对于我们的请求返回这样的错误信息： Bad Request (Invalid Header Name)
			 * 特此记录! 经过试验,我们千万不能加入"User-Agent"Header字段,虽然其他非.Text类型Blog支持我们的
			 * User-Agent,但是博客堂/博客园是不支持的!
			 */
			// hc.setRequestProperty("User-Agent",
			// "Profile/MIDP-1.0 Configuration/CLDC-1.0");
			// hc.setRequestProperty("Content-Length", "0");
			// hc.setRequestProperty("Accept-Language", "zh-cn");
			hc
					.setRequestProperty(
							"Accept",
							"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
			hc.setRequestProperty("Accept-Language", "zh-cn,zh;q=0.5");

			/*
			 * 设置了"Accept-Encoding"="gzip, deflate"时在访问cnblogs等utf8编码的rss时
			 * 会报如下错误java.io.UTFDataFormatException: invalid byte 11111011
			 * 可能的原因是采用gzip压缩传输时utf8不支持
			 *
			 */
			hc.setRequestProperty("Accept-Encoding", "gzip, deflate");
			hc.setRequestProperty("Accept-Charset",
					"gb18030,utf-8;q=0.7,*;q=0.7");
			// Accept:
			// text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

			hc.setRequestProperty("Connection", "close");

			/**
			 * Get a DataInputStream from the HttpConnection and forward it to
			 * kXML parser
			 */
			System.out.println("/** openInputStream to:" + url);
			System.out.println("encoding:" + hc.getEncoding());
			String responseMessage = hc.getResponseMessage();
			System.out.println("responseCode:" + hc.getResponseCode()
					+ "\tresponseMessage:" + responseMessage);
			if (hc.getResponseCode() == HttpConnection.HTTP_OK) {
				InputStream in = hc.openInputStream();
				if ("gzip".equals(hc.getEncoding())) {
					in = new GZIPInputStream(in);
				}
				bParseOk = parseRssFeedXml(in);
			} else {
				throw new java.lang.IllegalArgumentException("responseCode:"
						+ hc.getResponseCode() + "\tresponseMessage:"
						+ responseMessage);
			}
			// print(hc.openInputStream());
			// bParseOk = parseRssFeedXml(hc.openDataInputStream());
			// } catch (Exception e) {
			// e.printStackTrace();
			// throw new Exception("Error while parsing RSS data: " +
			// e.toString());
		} finally {
			if (hc != null)
				hc.close();
		}

		return bParseOk;
	}

	// private void print(InputStream is) throws Exception {
	// ByteArrayOutputStream byteArrayOutputStream = new
	// ByteArrayOutputStream();
	// OutputStreamWriter writer = new OutputStreamWriter(
	// byteArrayOutputStream);
	// IOUtils.copy(is, writer, "UTF8");
	// byte[] byteArray = byteArrayOutputStream.toByteArray();
	// System.out.println("byteArrayOutputStream:" + new String(byteArray));
	//
	// }

	/***************************************************************************
	 * // RssFeedParser::parseRssFeedXml // // Description: Nasty RSS feed XML
	 * parser. Seems to work with both RSS 1.0 and 2.0. // // Parameters: //
	 * Return Values: // Remark: 下面我们看看KXML如何做一个Pull语法分析程序。
	 * 首先我们需要创建了一个XmlParser，并把它传到一个InputStream中。 其次，我们需要跳过RSS根节点的“rss”之类的东西。
	 * 下面，我们来寻找RSS中的第一个“item”节点， 它代表这个RSS文档中确实包含了新闻条目。这是通过下面的循环做到的。
	 * parser.next()的含义是“Get next parsing event”，这样就可以遍历文档， 如果找不到item节点，就抛出一个异常。
	 *
	 * 确认有item节点后，我们来寻找RSS中代表每一个新闻的“item”节点下的三个节点“title”
	 * “link”“description”，这是通过下面的循环做到的。parser.next()的含义是 “Call next() event if
	 * it is START_TAG or END_TAG otherwise throw an exception”，
	 * 这样就可以遍历文档找全所有的新闻的主题、链接和摘要了。
	 *
	 * //创建人: Tommi Laukkanen //修改者: zhengyun@ultrapower 2005.10.07 //
	 **************************************************************************/
	public boolean parseRssFeedXml(InputStream is) throws IOException,
			XmlPullParserException {

		System.out.println("Enter parseRssFeedXml");

		// DataInputStream dis = new DataInputStream(is);
		// InputStreamReader isr=new InputStreamReader(dis, "utf8");

		/** Initialize item collection */
		m_rssFeed.getItems().removeAllElements();
		// System.out.println("End removeAllElements");

		/** Initialize XML parser and parse RSS XML */
		KXmlParser parser = new KXmlParser();
		// parser.setInput( new InputStreamReader(is));

		// boolean bHasXMLEntity = false;
		try {
			System.out.println("Begin setInput");
			// parser.setInput( is,"utf-8"); // 设置读取用UTF-8编码
			parser.setInput(is, null); // 设置null让KXmlParser自动检测该使用哪种编码

		} catch (org.xmlpull.v1.XmlPullParserException exc) {
			System.out.println("!-- 解析时发生异常: --!\nException:"
					+ exc.getMessage());
			parser.setInput(is, "utf-8");
		} catch (Throwable ex) {
			ex.printStackTrace();
			return false;
		} finally {
			System.out.println("End setInput");
		}

		// //////////////////////////////////////////////
		// / 下面的代码用于寻找当前RSS文档是否以<?xml...开头
		/*
		 * byte[] srcBuf = new byte[2]; int srcCount = is.read(srcBuf, 0,
		 * srcBuf.length); int nBufChar; if (srcCount <= 0) nBufChar = -1; else
		 * nBufChar = srcBuf[0]; if (nBufChar == '<') {
		 * System.out.println("确实找到了<"); nBufChar = srcBuf[1]; if (nBufChar ==
		 * '?') { System.out.println("确实找到了<?xml..."); bHasXMLEntity = true; } }
		 */
		// //////////////////////////////////////////////
		/** RSS item properties */
		String title = null;
		String description = null;
		String link = null;

		/*
		 * if(true == bHasXMLEntity) { // 这个nextTag()是为了跳过"<?xml..."的,但是有许多RSS不规范,并没有生成<?xml...,在这种情况下 //
		 * ,往往会在这句话上失败,并报告说“org.xmlpull.v1.XmlPullParserException: PI must not
		 * start with xml” parser.nextTag(); System.out.println("执行nextTag跳过<?xml..."); }
		 */
		try {
			System.out.println("error in nextTag" + parser.getInputEncoding());
			parser.nextTag();
		} catch (Throwable t) {
			System.out.println("error in nextTag");
			t.printStackTrace();
			return false;
		}
		System.out.println("执行nextTag");
		/** Various tags... Wait for the <item> tag */
		parser.require(XmlPullParser.START_TAG, null, null);
		System.out.println("执行require START_TAG");

		/*
		 * 如果你看到“Error: Error while parsing RSS data: w: unresolved:
		 * (position:TEXT @14:224 in
		 * java.io.InputStreamReader@ea0ef881”的错误，那么是不是前面还输出了许多XML的节点名称，那么是否有“#==XML
		 * Node Name:html”这样的输出呢？ 如果有，这表明该Rss Feed返回的并不是xml，而是html文档。
		 */
		while (!"item".equals(parser.getName())) {
			String strTagName = parser.getName();
			if (strTagName != null) {
				System.out.println("#==XML Node Name:" + strTagName);
				if (strTagName.toLowerCase().compareTo("html") == 0) {
					String sError = "这并不是Rss Feed，返回的是HTML文档!";
					Object[] args = { sError };
					controller.handleEvent(
							GUIController.EventID.EVENT_PARSE_RSS_ERROR, args);
					return false;
				}
			}
			// System.out.println("#==XML Node Text:" + parser.getText());
			/** Check if document doesn't include any item tags */
			if (parser.next() == XmlPullParser.END_DOCUMENT) {
				String sError = "该Rss Feed没有发布任何文章!";
				Object[] args = { sError };
				controller.handleEvent(
						GUIController.EventID.EVENT_PARSE_RSS_ERROR, args);
				return false;
			}
		}

		/** Parse <item> tags */
		do {
			parser.require(XmlPullParser.START_TAG, null, null);

			/** Initialize properties */
			title = "";
			description = "";
			link = "";

			/** One <item> tag handling */
			while (parser.nextTag() != XmlPullParser.END_TAG) {
				parser.require(XmlPullParser.START_TAG, null, null);
				String name = parser.getName();
				String text = parser.nextText();

				/** Save item property values */
				if (name.equals("title"))
					title = text;
				else if (name.equals("description"))
					description = text;
				else if (name.equals("link"))
					link = text;

				parser.require(XmlPullParser.END_TAG, null, name);
			}

			/** Debugging information */
			System.out.println("Title:       " + title);
			// System.out.println ("Link: " + link);
			// System.out.println ("Description: " + description);

			/**
			 * Create new RSS item and add it do RSS document's item collection
			 */
			RssItem rssItem = new RssItem(title, link, description);
			m_rssFeed.getItems().addElement(rssItem);

			openForm.appendRssItem(title);

			parser.nextTag();

		} while ("item".equals(parser.getName()));

		parser = null;

		return true;
	}
}
